﻿using Microsoft.SemanticKernel;
using ModelContextProtocol.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;


    public static class ModelContextProtocolExtensions
    {
        

        public static KernelFunction ToKernelFunction(this McpClientTool tool, IMcpClient mcpClient, CancellationToken cancellationToken=default)
        {
            async Task<string> InvokeToolAsync(Kernel kernel, KernelFunction function, KernelArguments arguments, CancellationToken ct)
            {
                try
                {
                    // Convert arguments to dictionary format expected by ModelContextProtocol
                    Dictionary<string, dynamic?> mcpArguments = [];
                    foreach (var arg in arguments)
                    {
                        if (arg.Value is not null)
                        {
                            mcpArguments[arg.Key] = function.ToArgumentValue(arg.Key, arg.Value);
                        }
                    }

                    // Call the tool through ModelContextProtocol
                    var result = await mcpClient.CallToolAsync(
                      tool.Name,
                      mcpArguments.AsReadOnly(),
                      cancellationToken: ct
                    ).ConfigureAwait(false);

                    // Extract the text content from the result
                    return string.Join("\n", result.Content
              .Where(c => c.Type == "text")
              .Select(c => c.Text));
                }
                catch (Exception ex)
                {
                    await Console.Error.WriteLineAsync($"Error invoking tool '{tool.Name}': {ex.Message}");

                    // Rethrowing to allow the kernel to handle the exception
                    throw;
                }
            }

            return KernelFunctionFactory.CreateFromMethod(
               method: InvokeToolAsync,
               functionName: tool.Name.Replace("-", "_"),
               description: tool.Description,
               parameters: tool.ToParameters(),
               returnParameter: ToReturnParameter()
             );
        }
        private static object ToArgumentValue(this KernelFunction function, string name, object value)
        {
        var parameterType = function.Metadata.Parameters.FirstOrDefault(p => p.Name == name)?.ParameterType;

        var typeee = Nullable.GetUnderlyingType(parameterType);
        // 尝试使用ChangeType进行转换
        if (value == null || parameterType.IsInstanceOfType(value))
        {
            return value;
        }
        try
        {
            return Convert.ChangeType(value, typeee ?? parameterType);
        }
        catch (Exception ex)
        {
            return value;
        }
        //if (parameterType == null)
        //{
        //    return value;
        //}

        //if (Nullable.GetUnderlyingType(parameterType) == typeof(int))
        //{
        //    return Convert.ToInt32(value);
        //}

        //if (Nullable.GetUnderlyingType(parameterType) == typeof(double))
        //{
        //    return Convert.ToDouble(value);
        //}

        //if (Nullable.GetUnderlyingType(parameterType) == typeof(bool))
        //{
        //    return Convert.ToBoolean(value);
        //}

        //if (parameterType == typeof(List<string>))
        //{
        //    return (value as IEnumerable<object>)?.ToList() ?? value;
        //}

        //if (parameterType == typeof(Dictionary<string, object>))
        //{
        //    return (value as Dictionary<string, object>)?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? value;
        //}
        // return value;
    }

        private static List<KernelParameterMetadata>? ToParameters(this McpClientTool tool)
        {
            var inputSchema = JsonSerializer.Deserialize<JsonSchema>(tool.JsonSchema.GetRawText());
            var properties = inputSchema?.Properties;
            if (properties == null)
            {
                return null;
            }

            HashSet<string> requiredProperties = [.. inputSchema!.Required ?? []];
            return properties.Select(kvp =>
              new KernelParameterMetadata(kvp.Key)
              {
                  Description = kvp.Value.Description,
                  ParameterType = ConvertParameterDataType(kvp.Value, requiredProperties.Contains(kvp.Key)),
                  IsRequired = requiredProperties.Contains(kvp.Key)
              }).ToList();
        }

        private static KernelReturnParameterMetadata ToReturnParameter()
        {
            return new KernelReturnParameterMetadata
       
    {
                ParameterType = typeof(string),
        };
        }

        private static Type ConvertParameterDataType(JsonSchemaProperty property, bool required)
        {
            var type = property.Type switch
            {
                "string" => typeof(string),
                "integer" => typeof(int),
                "number" => typeof(double),
                "boolean" => typeof(bool),
                "array" => typeof(List<string>),
                "object" => typeof(Dictionary<string, object>),
                _ => typeof(object)
            };

            return !required && type.IsValueType ? typeof(Nullable<>).MakeGenericType(type) : type;
        }

    }

internal class JsonSchema
{
    /// <summary>
            /// The type of the schema, should be "object".
            /// </summary>
    [JsonPropertyName("type")]
    public string Type { get; set; } = "object";

    /// <summary>
            /// Map of property names to property definitions.
            /// </summary>
    [JsonPropertyName("properties")]
    public Dictionary<string, JsonSchemaProperty>? Properties { get; set; }

    /// <summary>
            /// List of required property names.
            /// </summary>
    [JsonPropertyName("required")]
    public List<string>? Required { get; set; }
}


internal class JsonSchemaProperty
{
    /// <summary>
            /// The type of the property. Should be a JSON Schema type and is required.
            /// </summary>
    [JsonPropertyName("type")]
    public string Type { get; set; } = string.Empty;

    /// <summary>
            /// A human-readable description of the property.
            /// </summary>
    [JsonPropertyName("description")]
    public string? Description { get; set; } = string.Empty;
}